/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-08-14     Jackistang   add comments for function interface.
 */
#ifndef RINGBUFFER_H__
#define RINGBUFFER_H__

#ifdef __cplusplus
extern "C" {
#endif
#include "rtdevice.h"

/* ring buffer */
struct rt_ringbuffer {
	rt_uint8_t* buffer_ptr;
	/* use the msb of the {read,write}_index as mirror bit. You can see this as
	 * if the buffer adds a virtual mirror and the pointers point either to the
	 * normal or to the mirrored buffer. If the write_index has the same value
	 * with the read_index, but in a different mirror, the buffer is full.
	 * While if the write_index and the read_index are the same and within the
	 * same mirror, the buffer is empty. The ASCII art of the ringbuffer is:
	 *
	 *          mirror = 0                    mirror = 1
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Full
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 *  read_idx-^                   write_idx-^
	 *
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Empty
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 * read_idx-^ ^-write_idx
	 *
	 * The tradeoff is we could only use 32KiB of buffer for 16 bit of index.
	 * But it should be enough for most of the cases.
	 *
	 * Ref: http://en.wikipedia.org/wiki/Circular_buffer#Mirroring */
	rt_uint16_t read_mirror : 1;
	rt_uint16_t read_index : 15;
	rt_uint16_t write_mirror : 1;
	rt_uint16_t write_index : 15;
	/* as we use msb of index as mirror bit, the size should be signed and
	 * could only be positive. */
	rt_int16_t buffer_size;
};

enum rt_ringbuffer_state {
	RT_RINGBUFFER_EMPTY,
	RT_RINGBUFFER_FULL,
	/* half full is neither full nor empty */
	RT_RINGBUFFER_HALFFULL,
};

/**
 * RingBuffer for DeviceDriver
 *
 * Please note that the ring buffer implementation of RT-Thread
 * has no thread wait or resume feature.
 */
void rt_ringbuffer_init(struct rt_ringbuffer* rb, rt_uint8_t* pool, rt_int16_t size);
void rt_ringbuffer_reset(struct rt_ringbuffer* rb);
rt_size_t rt_ringbuffer_put(struct rt_ringbuffer* rb, const rt_uint8_t* ptr, rt_uint16_t length);
rt_size_t rt_ringbuffer_put_force(struct rt_ringbuffer* rb, const rt_uint8_t* ptr, rt_uint16_t length);
rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer* rb, const rt_uint8_t ch);
rt_size_t rt_ringbuffer_putchar_force(struct rt_ringbuffer* rb, const rt_uint8_t ch);
rt_size_t rt_ringbuffer_get(struct rt_ringbuffer* rb, rt_uint8_t* ptr, rt_uint16_t length);
rt_size_t rt_ringbuffer_peak(struct rt_ringbuffer* rb, rt_uint8_t** ptr);
rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer* rb, rt_uint8_t* ch);
rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer* rb);

void rt_assert_handler(const char* ex_string, const char* func, rt_size_t line);

#define RT_ASSERT(EX)                           		\
	if (!(EX))                                    		\
	{                                                   \
		rt_assert_handler(#EX, __FUNCTION__, __LINE__); \
	}


#ifdef RT_USING_HEAP
struct rt_ringbuffer* rt_ringbuffer_create(rt_uint16_t length);
void rt_ringbuffer_destroy(struct rt_ringbuffer* rb);
#endif

/**
 * @brief Get the buffer size of the ring buffer object.
 *
 * @param rb        A pointer to the ring buffer object.
 *
 * @return  Buffer size.
 */
rt_inline rt_uint16_t rt_ringbuffer_get_size(struct rt_ringbuffer* rb) {
	RT_ASSERT(rb != RT_NULL);
	return rb->buffer_size;
}

/** return the size of empty space in rb */
#define rt_ringbuffer_space_len(rb) ((rb)->buffer_size - rt_ringbuffer_data_len(rb))


#ifdef __cplusplus
}
#endif

#endif
